/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package image.review.components;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.ReentrantLock;
import javax.imageio.ImageIO;

/**
 *
 * @author troels
 */
public class ImageBuffer {

    private BufferedImage[] images;
    private File[] files;
    private int imageIndex;
    private int bufferRange;
    private Queue<ImageToLoad> pending;
    private ReentrantLock lock = new ReentrantLock();
    private Thread thread1;
    private Thread thread2;
    private BufferedImage imageHolder;

    /**
     * 
     * @param files
     * @param bufferRange
     */
    public ImageBuffer(File[] files, int bufferRange) {
        
        images = new BufferedImage[1 + 2 * bufferRange];
        this.files = files;
        this.bufferRange = bufferRange;
        imageIndex = bufferRange * files.length;
        pending =  new LinkedList<>();
        bufferImages();
    }

    /**
     * 
     * @return
     */
    public BufferedImage getNext() {
        imageIndex++;
        return getCurrent();

    }

    public BufferedImage getCurrent() {
        bufferImages();
        imageHolder = images[imageIndex % (1 + 2 * bufferRange)];
        images[imageIndex % (1 + 2 * bufferRange)] = null;
        return imageHolder;
    }

    /**
     * 
     * @return
     */
    public BufferedImage getPrevious() {
        imageIndex--;
        return getCurrent();
    }

    /**
     * 
     * @param index
     * @return
     */
    public BufferedImage getAtIndex(int index) {

        if (Math.abs(imageIndex - index % index) <= bufferRange) {
            imageIndex = bufferRange * files.length + index;
            imageHolder = getCurrent();
            images = new BufferedImage[1 + 2 * bufferRange];
            images[imageIndex % (1 + 2 * bufferRange)] = imageHolder;
            return imageHolder;
        }
        else {
            try {
                imageIndex = index;
                bufferImages();
                return ImageIO.read(files[index]);
            }
            catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 
     * @return
     */
    public File[] getFiles() {
        return files;
    }

    /**
     * 
     * @param files
     */
    public void setFiles(File[] files) {

        images = new BufferedImage[files.length];
        this.files = files;
    }

    private void bufferImages() {
        if (images[imageIndex % (1 + 2 * bufferRange)] == null) {
            System.out.println(files.length);
            pending.add(new ImageToLoad(files[imageIndex % files.length], imageIndex % (1 + 2 * bufferRange)));
        }
        for (int i = 1; i <= bufferRange; i++) {
            if (images[(imageIndex + i) % (1 + 2 * bufferRange)] == null) {
                pending.add(new ImageToLoad(files[(imageIndex + i) % files.length], (imageIndex + 1) % (1 + 2 * bufferRange)));
            }
            if (images[(imageIndex - i) % (1 + 2 * bufferRange)] == null) {
                pending.add(new ImageToLoad(files[(imageIndex - i) % files.length], (imageIndex - i) % (1 + 2 * bufferRange)));
            }
        }
        thread1 = new Thread(new ThreadLoader(this));
        thread1.start();
        thread2 = new Thread(new ThreadLoader(this));
        thread2.start();
    }

    private ImageToLoad getNextPending() {
        lock.lock();
        ImageToLoad returnImage;
        try {
            returnImage = pending.poll();
        }
        finally {
            lock.unlock();
        }

        return returnImage;
    }

    private void addToPending(ImageToLoad imageToLoad) {
        lock.lock();
        try {
            pending.add(imageToLoad);
        }
        finally {
            lock.unlock();
        }
    }

    private class ThreadLoader implements Runnable {

        ImageBuffer imageBuffer;
        ImageToLoad imageToLoad;

        ThreadLoader(ImageBuffer imageBuffer) {
            this.imageBuffer = imageBuffer;
        }

        @Override
        public void run() {
            while ((imageToLoad = imageBuffer.getNextPending()) != null) {
                try {
                    images[imageToLoad.index % 1 + 2 * bufferRange] = ImageIO.read(imageToLoad.file);
                    System.out.println("Image " + imageToLoad.file.toString() + " loaded. " + images[imageToLoad.index % 1 + 2 * bufferRange]);
                }
                catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    private class ImageToLoad {

        private final File file;
        private final int index;

        ImageToLoad(File file, int index) {
            this.file = file;
            this.index = index;
        }

        public File getFile() {
            return file;
        }

        public int getIndex() {
            return index;
        }
    }
}
